Перейти к основному содержимому

2.01. Справочник по iOS

Разработчику Архитектору Инженеру

Справочник по iOS

📘 Core App Metadata, Launch Configuration & Runtime Environment

1.1. Info.plist — основные ключи и допустимые значения

Все ключи в Info.plist являются строками, но значения — строго типизированы: <string>, <integer>, <array>, <dict>, <bool>, <data>.

Ключ (ключ в plist / CFBundle)Тип значенияОписаниеВозможные значения / ФорматПримечания
CFBundleDisplayNamestringОтображаемое имя приложения (локализуемо через InfoPlist.strings)MyAppПриоритет выше, чем у CFBundleName.
CFBundleNamestringКраткое имя (≤15 символов для Springboard)MyAppИспользуется при невозможности отобразить DisplayName.
CFBundleIdentifierstringУникальный bundle ID в reverse-DNS форматеcom.example.MyAppНеизменяем после публикации в App Store.
CFBundleVersionstringBuild number (технический номер сборки)1, 1.2.3, 20251121.1Должен возрастать монотонно при каждой сборке.
CFBundleShortVersionStringstringMarketing version (версия для пользователя)1.0, 2.1.5Соответствует version в App Store Connect.
LSRequiresIPhoneOSbooleanПриложение требует iOS (а не macOS/iPadOS в legacy-режиме)trueОбязательно для всех современных iOS-приложений.
UIApplicationSceneManifestdictМанифест сцен (для многозадачности, iPad, macOS Catalyst)словарь с UISceneConfigurationsИспользуется вместо UISupportedInterfaceOrientations при наличии нескольких сцен.
UISupportedInterfaceOrientationsarray<string>Поддерживаемые ориентации (устаревшее для сцен)UIInterfaceOrientationPortrait, UIInterfaceOrientationLandscapeLeft, UIInterfaceOrientationPortraitUpsideDown, UIInterfaceOrientationLandscapeRightПрименяется только если не используется SceneDelegate.
UISupportedInterfaceOrientations~ipadarray<string>То же, но для iPadкак выше
UIRequiredDeviceCapabilitiesarray<string>Жёсткие требования к железуarm64, metal, gps, telephony, wifi, accelerometer, gyro, nfc, camera-front, camera-rear, gamekit, location-services, microphone, opengles-3, opengles-2, bluetooth-le, arkitПриложение не установится, если устройство не поддерживает хотя бы один из указанных. Можно использовать !-префикс для исключения (редко, например !telephony для iPad-only).
UIBackgroundModesarray<string>Режимы фоновой работы (требуют entitlement'ов и justification в App Review)audio, location, voip, fetch, remote-notification, bluetooth-central, bluetooth-peripheral, background-processing, continuous-waveform-processing, app-eventsТребуется описание использования в App Store Connect.
NSLocationWhenInUseUsageDescriptionstringЗапрос доступа к геолокации «во время использования»текст на локализованном языкеОбязателен при вызове requestWhenInUseAuthorization().
NSLocationAlwaysAndWhenInUseUsageDescriptionstringДля requestAlwaysAuthorization()текстБез этого ключа запрос always игнорируется.
NSCameraUsageDescriptionstringОбоснование использования камерытекст
NSPhotoLibraryUsageDescriptionstringДоступ к медиабиблиотекетекст
NSMicrophoneUsageDescriptionstringДоступ к микрофонутекст
NSBluetoothAlwaysUsageDescriptionstringДля CoreBluetooth в фоне (начиная с iOS 13)текст
NSBluetoothPeripheralUsageDescriptionstringДля iOS ≤12 / совместимоститекст
NSAppTransportSecuritydictATS (App Transport Security) настройкиNSAllowsArbitraryLoads: <bool>, NSExceptionDomains: <dict<domain, dict>>Рекомендуется не отключать NSAllowsArbitraryLoads, а использовать исключения.
ITSAppUsesNonExemptEncryptionbooleanИспользует ли приложение криптографию, подпадающую под экспортные ограничения СШАtrue/falseЕсли false — не требуется подача EAR99 declaration.
UIFileSharingEnabledbooleanВключает файловый шеринг через iTunes/Files.apptrueДаёт доступ к Documents/ через Files.app.
LSSupportsOpeningDocumentsInPlacebooleanПоддержка прямого редактирования документов (для UIDocument)trueТребуется вместе с UIFileSharingEnabled для iCloud-документов.
UIApplicationSupportsIndirectInputEventsbooleanПоддержка внешних клавиатур/контроллеров (iPadOS/macOS Catalyst)trueИначе события могут обрабатываться некорректно.
UIUserInterfaceStylestringПринудительная тема интерфейсаLight, Dark, AutomaticAutomatic — значение по умолчанию.
NSHumanReadableCopyrightstringАвторские права (отображается в App Store)© 2025 Timur Tagirov
CFBundleDevelopmentRegionstringЯзык разработки по умолчанию (для локализации)en, ru, BaseИспользуется, если локализация не найдена.
CFBundleLocalizationsarray<string>Список поддерживаемых языков["en", "ru", "es"]Влияет на порядок выбора локали.
CFBundleURLTypesarray<dict>Поддержка кастомных URL-схем[ { "CFBundleURLName": "com.example.myapp", "CFBundleURLSchemes": ["myapp"] } ]Регистрация myapp:// scheme.
LSApplicationQueriesSchemesarray<string>Схемы, которые приложение может проверять через canOpenURL:["whatsapp", "tg", "vk", "fb"]Без этого canOpenURL: вернёт false даже если приложение установлено.

1.2. Launch Options (application(_:didFinishLaunchingWithOptions:))

При запуске приложения система передаёт словарь launchOptions — ключи из UIApplication.LaunchOptionsKey.

Ключ (Swift: UIApplication.LaunchOptionsKey)Тип значенияОписаниеКогда появляется
.annotationAnyПользовательские данные, переданные через UIApplication.open(_:options:completionHandler:)При открытии через custom URL или Universal Link с аннотацией
.bluetoothCentralsKey[String]UUID'ы центральных менеджеров CoreBluetoothПри восстановлении после терминирования в фоне
.bluetoothPeripheralsKey[String]UUID'ы периферийных устройствТо же
.cloudKitShareMetadataKeyCKShare.MetadataМетаданные приглашения CloudKitПри открытии shared-контента
.locationKeyCLLocationПоследняя известная локацияПри запуске из-за significant location change или region monitoring
.localNotificationKeyUILocalNotification (iOS ≤9)Устарело
.remoteNotificationKey[AnyHashable : Any]Payload APNsПри запуске через push-уведомление с "content-available": 1 или interactive push
.sourceApplicationString (bundle ID)Bundle ID приложения-инициатораПри открытии через URL scheme или Universal Link из другого приложения
.urlURLURL, по которому открыто приложениеmyapp://action?id=123, https://example.com/deep-link
.userActivityDictionaryKey[String: NSUserActivity]Activity для Handoff / SpotlightПри продолжении activity
.userActivityTypeStringТип activity (устаревший способ)

⚠️ Начиная с iOS 13, если используется SceneDelegate — launch options приходят в scene(_:willConnectTo:options:) через UIScene.ConnectionOptions.


1.3. Application State Properties (UIApplication.shared.applicationState)

Значение (UIApplication.State)ОписаниеТипичные действия
.activeПриложение на переднем плане и получает событияПолный UI-ввод, анимации, обновление данных в реальном времени
.inactiveПриложение на переднем плане, но не получает событияВызов Siri, Control Center, incoming call, переход в multitasking, lock screen. Следует паузить таймеры/аудио/камеру.
.backgroundПриложение в фоне и может выполнять ограниченные задачиФоновые fetch, location updates, Bluetooth, completion handlers. По истечении ~30 сек (beginBackgroundTask) — suspend.

Изменения состояния улавливаются через делегаты:

  • applicationWillResignActive(_:)
  • applicationDidEnterBackground(_:)
  • applicationWillEnterForeground(_:)
  • applicationDidBecomeActive(_:)

1.4. Основные runtime environment attributes (доступны через ProcessInfo, UIDevice, UIScreen)

ИсточникСвойствоТипПример значенияПримечание
ProcessInfo.processInfo.environment["XPC_SERVICE_NAME"]String?"UIKitApplication:com.example.MyApp[0xdeadbeef]"Идентификатор процесса в XPC
.operatingSystemVersionOperatingSystemVersionmajorVersion: 18, minorVersion: 1, patchVersion: 0Не используйте UIDevice.systemVersion — он устарел и возвращает строку, подверженную spoofing'у.
.hostnameString?"Timurs-iPhone"
.globallyUniqueStringStringуникальный UUID-like стрингДля временных имён файлов/директорий
UIDevice.current.nameString"iPhoneTimur"Настраивается пользователем — ненадёжно
.modelString"iPhone", "iPad"Не даёт конкретной модели (см. ниже)
.localizedModelString"iPhone", "iPod touch"
.systemNameString"iOS"Или "iPadOS" начиная с 13
.identifierForVendorUUID?A8D5D2E1-...Сбрасывается при удалении всех приложений vendor’а.
.isBatteryMonitoringEnabled + .batteryLevel, .batteryStateFloat, UIDevice.BatteryState0.75, .chargingТребует включения мониторинга
UIScreen.main.bounds, .nativeBoundsCGRect{x:0 y:0 w:390 h:844} (iPhone 14 Pro)nativeBounds — в physical pixels
.scaleCGFloat3.0Для Retina HD (iPhone 14 Pro)
.nativeScaleCGFloat3.0То же, но всегда physical/native
.preferredMode / .availableModesUIScreen.Modeширина/высота в pixels, refresh rateНапример: { width: 1170, height: 2532, pixelFormat: 875704916, refreshRate: 120.0 }
NSProcessInfo.processNameString"MyApp"Имя executable по умолчанию — CFBundleExecutable.

🔍 Для определения конкретной модели устройства (iPhone16,3 и т.п.) — читайте hw.machine через sysctlbyname("hw.machine", ...). Примеры:


📘 UI/UIKit Core

Фокус — на публичных, часто используемых, диагностически важных свойствах, включая runtime-only атрибуты (например, доступные только в debug-режиме через private API, если они стабильны и полезны для анализа).
Все свойства приведены в формате Swift, с типами и семантическими пояснениями.
Где уместно — указаны аналоги в SwiftUI (через UIViewRepresentable/UIViewControllerRepresentable или environment keys).


2.1. UIView — основные свойства

СвойствоТипОписаниеЗначения / Особенности
frameCGRectПозиция и размер в координатах superviewИзменение frame пересчитывает bounds.origin и center. Не используйте при наличии transform ≠ .identity.
boundsCGRectЛокальная система координат: origin — точка (0,0) вьюхи, size — её размерbounds.origin влияет на contentOffset потомков (например, для скролла вручную).
centerCGPointЦентр вьюхи в координатах superviewПредпочтительный способ позиционирования при анимации центра.
transformCGAffineTransform2D-трансформация (scale, rotate, translate)Не влияет на frame (становится «undefined»). При transform ≠ .identityframe игнорируется layout engine’ом.
layer.transformCATransform3D3D-трансформация (layer-only)Не затрагивает layout (bounds/frame сохраняют «логическое» положение).
alphaCGFloatПрозрачность (0.01.0)Значения < 0.01 приводят к isHidden = true на уровне GPU (оптимизация).
isHiddenBoolВидимость (не влияет на layout)Скрытые вьюхи не получают hitTest, не анимируются.
isUserInteractionEnabledBoolОбработка касаний (true по умолчанию)Значение false блокирует всеgesture recognizers поддерева.
clipsToBoundsBoolОбрезка содержимого за пределы boundsЭквивалент layer.masksToBounds.
autoresizingMaskUIView.AutoresizingMaskУстаревший механизм autosizing (pre-Auto Layout).flexibleWidth, .flexibleHeight, .flexibleTopMargin и др. Не смешивать с constraints.
translatesAutoresizingMaskIntoConstraintsBoolСоздавать ли constraints из autoresizingMaskОбязательно false при использовании программных constraints. По умолчанию — true.
semanticContentAttributeUISemanticContentAttributeНаправление контента (для LTR/RTL).unspecified, .playback, .spatial, .forceLeftToRight, .forceRightToLeft
layoutMarginsNSDirectionalEdgeInsetsОтступы от краёв для layout (с учётом safe area)Чтение: системные (например, от notch). Запись: если preservesSuperviewLayoutMargins = false.
directionalLayoutMarginsNSDirectionalEdgeInsetsТо же, но всегда directional (top, leading, bottom, trailing)Предпочтительнее layoutMargins.
insetsLayoutMarginsFromSafeAreaBoolВключать ли safe area в layoutMarginsПо умолчанию true.
tintColorUIColorЦвет интерактивных элементов (buttons, bars)Наследуется от window.tintColorrootViewController.view.tintColor → иерархия вьюх.
tintAdjustmentModeUIView.TintAdjustmentModeПоведение tint при inactive state.automatic (меняется на grayscale в background/inactive), .normal, .dimmed
contentModeUIView.ContentModeПоведение draw(_:) и sublayers при изменении bounds.scaleToFill, .scaleAspectFit, .scaleAspectFill, .redraw, .center, .top, .bottomRight и др.
backgroundColorUIColor?Фон (рендерится через layer)nil — прозрачный. Не влияет на isOpaque — его нужно выставлять отдельно.
isOpaqueBoolПодсказка рендереру: вьюха не содержит прозрачных пикселейУскоряет композитинг. По умолчанию true, если backgroundColor непрозрачный.
contentScaleFactorCGFloatМасштаб для draw(_:) (pixels per point)По умолчанию = UIScreen.main.scale. Можно увеличить для high-res custom drawing.
gestureRecognizers[UIGestureRecognizer]?Все прикреплённые recognizersТолько чтение. Добавлять через addGestureRecognizer(_:).
superview, subviews, windowUIView?, [UIView], UIWindow?Иерархияwindownil означает, что вьюха визуализируется на экране.
canBecomeFocusedBoolПоддержка focus engine (TV/iPadOS with pointer)true по умолчанию для UIButton, UITextField, UIControl. Переопределяется в кастомных вьюхах.
focusEffect UIFocusEffect?Эффект фокуса (например, UIFocusHaloEffect())Можно кастомизировать или отключить (nil).

🔧 Debug-only (через po view.value(forKey: "_...") в lldb):

  • _layerContentsFormat: "BGRA8888", "RGBA16F" — формат буфера rendering
  • _viewDelegate: UIViewController? — контроллер, отвечающий за вьюху
  • _gestureInfo: внутренний объект recognizers
  • _layerTreeAsASCIIString: текстовое дерево layers (удобно для анализа overdrawing)

2.2. UIViewController

СвойствоТипОписание
viewUIViewОсновной view controller’а. Ленивая инициализация через loadView()/viewDidLoad().
viewIfLoadedUIView?view, если уже загружен, иначе nil. Безопасный способ проверить, не вызывая загрузку.
isViewLoadedBoolБыл ли вызван loadView() (даже если viewDidLoad() ещё не сработал).
parentUIViewController?Родительский VC (например, в UINavigationController, UITabBarController, container VC).
children[UIViewController]Дочерние VC (через containment API: addChild(), removeFromParent()).
navigationControllerUINavigationController?Если VC находится в стеке навигации.
tabBarControllerUITabBarController?Аналогично.
splitViewControllerUISplitViewController?Для iPad/multitasking.
traitCollectionUITraitCollectionАктуальные traits (см. ниже).
overrideTraitCollectionUITraitCollection?Принудительная подмена traits для child VC (например, .init(userInterfaceStyle: .dark)).
modalPresentationStyleUIModalPresentationStyleСтиль модального показа: .fullScreen, .pageSheet, .formSheet, .popover, .overFullScreen, .automatic.
modalTransitionStyleUIModalTransitionStyleАнимация входа: .coverVertical, .flipHorizontal, .crossDissolve, .partialCurl.
isBeingPresented, isBeingDismissed, isMovingToParent, isMovingFromParentBoolФлаги во время переходов. Полезны в viewWillAppear(_:) для определения контекста.
hidesBottomBarWhenPushedBoolСкрывать tab bar при пуш в навигации.
definesPresentationContextBoolОпределяет, будет ли этот VC «владельцем» presentation context’а (например, для search bar в navigation bar).
providesPresentationContextTransitionStyleBoolИспользовать modalTransitionStyle этого VC при модальном показе из него.
navigationItemUINavigationItemКонтент navigation bar (title, buttons, search bar).
toolbarItems[UIBarButtonItem]?Элементы toolbar’а (если isToolbarHidden = false).
useToolbarBoolУстаревшее (iOS ≤4). Сейчас — navigationController?.setToolbarHidden(_:animated:).

⚠️ Lifecycle hooks (в порядке вызова):
loadView()viewDidLoad()viewWillAppear(_:)viewWillLayoutSubviews()viewDidLayoutSubviews()viewDidAppear(_:)
При уходе: viewWillDisappear(_:)viewDidDisappear(_:)
При low-memory: didReceiveMemoryWarning()
При rotation: viewWillTransition(to:with:), willTransition(to:with:)


2.3. UIWindow

СвойствоТипОписание
rootViewControllerUIViewController?Корневой VC. Замена требует анимации или makeKeyAndVisible().
windowSceneUIWindowScene?Сцена, к которой прикреплено окно (iOS 13+).
screenUIScreenЭкран, на котором отображается окно. При подключении внешнего дисплея — может отличаться от UIScreen.main.
overrideUserInterfaceStyleUIUserInterfaceStyleПринудительная тема для всего окна: .unspecified, .light, .dark.
tintColorUIColorКорневой tint-цвет приложения.
isKeyWindowBoolЯвляется ли окно активным (получает события).
windowLevelUIWindow.LevelZ-порядок: .normal, .alert, .statusBar, .overlay и др. Можно задавать кастомные (UIWindow.Level(rawValue: 100)).

2.4. UIGestureRecognizer

Общий интерфейс для всех recognizers (UITapGestureRecognizer, UIPanGestureRecognizer, UIRotationGestureRecognizer и др.)

СвойствоТипОписание
stateUIGestureRecognizer.State.possible, .began, .changed, .ended, .cancelled, .failed
viewUIView?Вьюха, к которой прикреплён recognizer.
delegateUIGestureRecognizerDelegate?Управление конфликтами (gestureRecognizer(_:shouldRecognizeSimultaneouslyWith:), gestureRecognizer(_:shouldBeRequiredToFailBy:) и др.)
isEnabledBoolВключён/выключен.
cancelsTouchesInViewBoolОтменять ли touchesBegan у вьюхи при распознавании (по умолчанию true для tap/long press).
delaysTouchesBegan, delaysTouchesEndedBoolЗадержка передачи событий вьюхе (по умолчанию true для pan/swipe).
require(toFail:)funcDependency chain: A требует, чтобы B провалился.

✅ Пример:

let pan = UIPanGestureRecognizer()
let tap = UITapGestureRecognizer()
pan.require(toFail: tap) // pan сработает, только если tap не распознался

2.5. UIControl

СвойствоТипОписание
isEnabledBoolСостояние: enabled/disabled (влияет на tintColor, alpha, взаимодействие).
isSelectedBoolЛогическое выделение (например, segmented control, custom checkbox).
isHighlightedBoolВременное состояние при нажатии (визуальная обратная связь).
stateUIControl.StateКомбинация флагов: normal, highlighted, disabled, selected, focused, application, reserved.
contentVerticalAlignment, contentHorizontalAlignmentUIControl.ContentVerticalAlignment, ...Horizontal...Выравнивание содержимого внутри bounds.
sendActions(for:)funcРучная отправка событий (touchUpInside, valueChanged, editingChanged и др.).
addTarget(_:action:for:)funcПодписка на события.
allTargets, allControlEventsSet<AnyHashable>, UIControl.EventДиагностика: кто подписан и на что.

2.6. UIScrollView

СвойствоТипОписание
contentSizeCGSizeРазмер «документа» внутри скролла.
contentOffsetCGPointТекущая позиция прокрутки (отрицательные значения — overscroll/bounce).
contentInsetUIEdgeInsets / NSDirectionalEdgeInsetsОтступы содержимого (например, под navigation bar).
scrollIndicatorInsetsUIEdgeInsetsОтступы для индикаторов скролла (часто = contentInset).
isScrollEnabledBoolМожно ли скроллить.
isDirectionalLockEnabledBoolБлокировать вторую ось при начале скролла по одной (как в Safari).
bouncesBoolРазрешить «подпрыг» за краями.
alwaysBounceVertical, alwaysBounceHorizontalBoolПоказывать bounce даже если contentSize ≤ bounds.
isPagingEnabledBoolПрокрутка строго по страницам (bounds.size).
showsHorizontalScrollIndicator, showsVerticalScrollIndicatorBoolОтображение индикаторов.
indicatorStyleUIScrollView.IndicatorStyle.default, .black, .white.
decelerationRateUIScrollView.DecelerationRate.normal, .fast, или кастомный CGFloat.
delegateUIScrollViewDelegate?scrollViewDidScroll(_:), scrollViewWillBeginDragging(_:), scrollViewDidEndDecelerating(_:) и др.
refreshControlUIRefreshControl?Pull-to-refresh (только для UITableView/UICollectionView, но хранится в scroll view).

2.7. UITableView & UICollectionView — ключевые свойства, общие и отличия

СвойствоТипUITableViewUICollectionView
dataSourceprotocolUITableViewDataSourceUICollectionViewDataSource
delegateprotocolUITableViewDelegateUICollectionViewDelegate
prefetchDataSourceprotocol?UITableViewDataSourcePrefetching?UICollectionViewDataSourcePrefetching?
estimatedRowHeight, estimatedSectionHeaderHeight, estimatedSectionFooterHeightCGFloatДля авто-расчёта высот (при rowHeight = UITableView.automaticDimension)
rowHeightCGFloatФиксированная высота строки
sectionHeaderHeight, sectionFooterHeightCGFloatАналогично
styleUITableView.Style.plain, .grouped, .insetGrouped
separatorStyleUITableViewCell.SeparatorStyle.none, .singleLine, .singleLineEtched
collectionViewLayoutUICollectionViewLayoutОбязательное свойство. Стандартные: UICollectionViewFlowLayout, NSCollectionLayoutSection (Compositional).
isPrefetchingEnabledBoolУстарело (в iOS 10 введено, но сейчас управляется через prefetchDataSource)true по умолчанию.
allowsSelection, allowsMultipleSelectionBool
allowsSelectionDuringEditingBool
indexPathsForSelectedItems[IndexPath]indexPathForSelectedRowМассив (multi-select).
visibleCells[UITableViewCell] / [UICollectionViewCell]Все видимые ячейки.
indexPathsForVisibleItems[IndexPath]indexPathsForVisibleRows
contentOffsetAdjustmentBehaviorUIScrollView.ContentOffsetAdjustmentBehavior (iOS 15+).automatic, .never — поведение при изменении contentInsetТо же (наследуется от UIScrollView).

🔁 Compositional Layout (iOS 13+) — ключевые объекты:

  • NSCollectionLayoutSection (orthogonalScrollingBehavior, contentInsets, interGroupSpacing)
  • NSCollectionLayoutGroup (.horizontal, .vertical, .custom)
  • NSCollectionLayoutItem (size: NSCollectionLayoutSize(widthDimension: ..., heightDimension: ...))
  • NSCollectionLayoutSize: .absolute(CGFloat), .estimated(CGFloat), .fractionalWidth(CGFloat), .fractionalHeight(CGFloat)
  • NSDirectionalEdgeInsets — для отступов.

2.8. UITraitCollection — полный список актуальных traits (iOS 18)

TraitТипВозможные значенияКомментарий
userInterfaceStyleUIUserInterfaceStyle.unspecified, .light, .darkМожет отличаться от системного (если overrideUserInterfaceStyle задан).
horizontalSizeClass, verticalSizeClassUIUserInterfaceSizeClass.unspecified, .compact, .regularНапример, iPhone в portrait: h=.compact, v=.regular; iPad split view: h=.compact, v=.regular.
displayScaleCGFloat1.0, 2.0, 3.0Аналог UIScreen.scale, но для окна/VC.
displayCornerRadiusCGFloat0.0, 13.0, 20.0, 30.0Радиус скругления экрана (для адаптации corner radius в UI).
preferredContentSizeCategoryUIContentSizeCategory.extraSmall, .small, .medium, .large, .extraLarge, .extraExtraLarge, .accessibilityMedium, ..., .accessibilityExtraExtraExtraLargeДинамические типы (UIFontMetrics).
legibilityWeightUIUserInterfaceLegibilityWeight.unspecified, .regular, .boldДля повышения контрастности (например, на lock screen).
forceTouchCapabilityUIForceTouchCapability.unknown, .available, .unavailable3D Touch (устарел, но property остался).
layoutDirectionUIUserInterfaceLayoutDirection.leftToRight, .rightToLeftОпределяет leading/trailing.
userInterfaceLevelUIUserInterfaceLevel.unspecified, .base, .elevatedДля адаптации под elevated UI (например, alert поверх основного контента).
accessibilityContrastUIAccessibilityContrast.unspecified, .normal, .highВысококонтрастный режим (Accessibility → Display).
accessibilityDifferentiateWithoutColorBoolИспользование форм/текстур вместо цвета.
accessibilityReduceTransparencyBoolОтключение blur-эффектов.
accessibilityReduceMotionBoolОтключение анимаций.
preferredLanguages[String]["ru-RU", "en-GB", "en"]Языки в порядке предпочтения (из Settings → Language).
accessibilityIgnoresInvertColorsBoolНе инвертировать цвета при Smart Invert.
elevationUIWindowScene.Elevation? (iOS 16+).primary, .secondary, .tertiaryДля сцен: main app, sheet, popover.
containerIdiomUIUserInterfaceIdiom.unspecified, .phone, .pad, .tv, .carPlay, .mac, .visionИдентификатор устройства, не зависит от размера окна (в отличие от size class).

✅ Пример использования:

override func traitCollectionDidChange(_ previousTraitCollection: UITraitCollection?) {
super.traitCollectionDidChange(previousTraitCollection)
if traitCollection.hasDifferentColorAppearance(comparedTo: previousTraitCollection) {
updateColors()
}
if traitCollection.preferredContentSizeCategory != previousTraitCollection?.preferredContentSizeCategory {
updateFonts()
}
}

📘 UI-компоненты

  • Accessibility Attributes
  • Core Animation Layer Properties (в контексте UIKit)

Фокус — на публичных, часто используемых и диагностически важных свойствах, включая ограничения поведения, взаимодействие с Auto Layout, особенности рендеринга и совместимость с SwiftUI/UIKit interoperability.


3.1. UILabel

СвойствоТипОписаниеОсобенности
textString?Текст (plain).При nil — пустая строка. Не интерпретирует HTML.
attributedTextNSAttributedString?Форматированный текст. Приоритет выше, чем text.Поддержка NSFontAttributeName, NSForegroundColorAttributeName, NSShadowAttributeName, NSLinkAttributeName, NSBaselineOffsetAttributeName, NSKernAttributeName, NSLigatureAttributeName, NSUnderlineStyleAttributeName, NSStrikethroughStyleAttributeName, NSBaselineOffsetAttributeName, NSTextEffectAttributeName, NSAttachmentAttributeName, NSObliquenessAttributeName, NSExpansionAttributeName.
fontUIFont?Шрифт.По умолчанию UIFont.systemFont(ofSize: 17). Использует UIFontMetrics.default.scaledFont(for:) для динамических типов (если adjustsFontForContentSizeCategory = true).
textColorUIColor?Цвет текста.По умолчанию .label (адаптивный под тему).
textAlignmentNSTextAlignment.left, .center, .right, .justified, .natural..natural → зависит от layoutDirection.
lineBreakModeNSLineBreakMode.byWordWrapping, .byCharWrapping, .byClipping, .byTruncatingHead, .byTruncatingTail, .byTruncatingMiddle.Влияет на отображение при numberOfLines = 1 и недостатке ширины.
numberOfLinesIntКоличество строк (0 = бесконечно).При 1 и lineBreakMode = .byTruncatingTail в конце.
adjustsFontSizeToFitWidthBoolАвтоуменьшение шрифта до minimumScaleFactor.Только при numberOfLines = 1.
minimumScaleFactorCGFloatМинимальный масштаб шрифта (относительно font.pointSize).По умолчанию 0.5.
baselineAdjustmentUIBaselineAdjustment.alignBaselines, .alignCenters, .none.Для выравнивания базовой линии при изменении размера.
highlightedTextColorUIColor?Цвет при isHighlighted = true.Редко используется (только если isUserInteractionEnabled = true и есть touches).
isHighlightedBoolВизуальное выделение (меняет textColorhighlightedTextColor).
isEnabledBoolСостояние (влияет на textColor, но не блокирует ввод — label не интерактивна по умолчанию).
shadowColor, shadowOffsetUIColor?, CGSizeТень текста.Простая, без blur (в отличие от layer shadow).
adjustsFontForContentSizeCategoryBoolАвтомасштабирование шрифта под Accessibility → Larger Text.Требует использования UIFontMetrics.
textRect(forBounds:limitedToNumberOfLines:)funcРасчёт bounding box текста без отрисовки.Полезно для кастомных draw(_:).
preferredMaxLayoutWidthCGFloatШирина, при которой пересчитывается высота (для multi-line в Auto Layout без width constraint).Устарело в пользу intrinsicContentSize + constraints, но иногда требуется для UITableViewCell в iOS ≤11.

⚠️ Ограничения:

  • UILabel не поддерживает интерактивные атрибуты в NSAttributedString (например, NSLinkAttributeName не кликабелен). Для этого — UITextView с isEditable = false, isSelectable = true.
  • При attributedText и adjustsFontSizeToFitWidth — поведение не определено (Apple не гарантирует работу).

3.2. UITextField

СвойствоТипОписание
text, attributedTextString?, NSAttributedString?Содержимое. Приоритет: attributedText > text.
placeholder, attributedPlaceholderString?, NSAttributedString?Текст-заполнитель. Приоритет: attributedPlaceholder.
font, textColor, textAlignment, minimumFontSizeАналогично UILabel.
clearButtonModeUITextField.ViewMode.never, .whileEditing, .unlessEditing, .always.
leftView, rightViewUIView?Кастомные вьюхи слева/справа (например, иконки).
leftViewMode, rightViewModeUITextField.ViewModeКогда показывать (.always, .unlessEditing, .whileEditing, .never).
inputViewUIView?Замена клавиатуры (например, UIPickerView).
inputAccessoryViewUIView?Панель над клавиатурой (часто — Done/Next toolbar).
keyboardTypeUIKeyboardType.default, .asciiCapable, .numbersAndPunctuation, .URL, .numberPad, .phonePad, .namePhonePad, .emailAddress, .decimalPad, .twitter, .webSearch, .asciiCapableNumberPad.
keyboardAppearanceUIKeyboardAppearance.default, .dark, .light.
returnKeyTypeUIReturnKeyType.default, .go, .google, .join, .next, .route, .search, .send, .yahoo, .done, .emergencyCall.
enablesReturnKeyAutomaticallyBoolДеактивировать Return, пока text пусто.
isSecureTextEntryBoolСкрытие ввода (звёздочки/кружки). Сбрасывает attributedText.
clearsOnBeginEditingBoolОчищать текст при фокусе.
clearsOnInsertionBoolОчищать при paste/диктовке (редко).
delegateUITextFieldDelegate?textFieldShouldBeginEditing(_:), textFieldDidBeginEditing(_:), textFieldShouldEndEditing(_:), textFieldDidEndEditing(_:), textField(_:shouldChangeCharactersIn:replacementString:), textFieldShouldClear(_:), textFieldShouldReturn(_:).
selectedTextRangeUITextRange?Текущее выделение (если isEditable = true).
markedTextRangeUITextRange?Диапазон составного ввода (например, при вводе кандзи).
allowsEditingTextAttributesBoolРазрешить форматирование через paste (если attributedText используется).
typingAttributes[NSAttributedString.Key : Any]Атрибуты для нового ввода (курсора).

⚠️ Важно:

  • UITextField не поддерживает multi-line. Для этого — UITextView.
  • При isSecureTextEntry = truesecureTextEntry сбрасывает font на системный (без кастомных шрифтов).
  • UITextField наследует UIControl, но не вызывает sendActions(for: .valueChanged) — только .editingChanged.

3.3. UITextView

СвойствоТипОписание
isEditableBoolРедактируемость. При false — не появляется клавиатура.
isSelectableBoolВозможность выделять и копировать текст (даже при isEditable = false).
dataDetectorTypesUIDataDetectorTypes.phoneNumber, .link, .address, .calendarEvent, .shipmentTrackingNumber, .flightNumber, .lookupSuggestion, .all, .none.
textContainerInsetUIEdgeInsetsОтступы текста от границ вьюхи.
textContainer.lineFragmentPaddingCGFloatГоризонтальный отступ строк (по умолчанию 5.0).
isScrollEnabledBoolМожно ли скроллить (наследуется от UIScrollView).
linkTextAttributes[NSAttributedString.Key : Any]Атрибуты для ссылок (при isSelectable = true).
allowsEditingTextAttributesBoolПоддержка копирования форматирования.
typingAttributes[NSAttributedString.Key : Any]Атрибуты ввода.
selectedRangeNSRangeТекущее выделение.
delegateUITextViewDelegate?textViewShouldBeginEditing(_:), textViewDidBeginEditing(_:), textViewShouldEndEditing(_:), textViewDidEndEditing(_:), textView(_:shouldChangeTextIn:replacementText:), textViewDidChange(_:), textViewDidChangeSelection(_:), textView(_:shouldInteractWith:in:interaction:).
inputDelegateUITextInputDelegate?Низкоуровневый протокол для кастомных input methods.

Для неинтерактивного rich text с ссылками используйте:

textView.isEditable = false
textView.isSelectable = true
textView.dataDetectorTypes = .link

3.4. UIImageView

СвойствоТипОписание
imageUIImage?Изображение (растровое или PDF).
highlightedImageUIImage?Изображение при isHighlighted = true.
isHighlightedBoolПереключает imagehighlightedImage.
isAnimatingBoolИдёт ли анимация (для animatedImage).
animationImages[UIImage]?Кадры анимации.
highlightedAnimationImages[UIImage]?Кадры при isHighlighted.
animationDurationTimeIntervalДлительность одного цикла анимации.
animationRepeatCountIntКоличество повторов (0 = бесконечно).
startAnimating(), stopAnimating()funcУправление анимацией.
tintColorUIColor?Применяется, если image?.renderingMode = .alwaysTemplate.

⚠️ Ограничения:

  • UIImageView не кэширует изображения — это ответственность UIImage.
  • При contentMode = .scaleAspectFill + clipsToBounds = true — стандартный способ «cover image».
  • Анимация через animationImages — не hardware-accelerated (избегать для >10 кадров или высокого FPS).

3.5. UIButton

Начиная с iOS 15, UIButton использует UIButton.Configuration (рекомендуемый путь). Ниже — свойства традиционного (UIButton(type:)) и нового (configuration) API.

Традиционный API (устаревший, но поддерживаемый)

СвойствоТипОписание
setTitle(_:for:), title(for:)String?, UIControl.StateЗаголовок для состояния.
setImage(_:for:), image(for:)UIImage?, UIControl.StateИконка слева от текста.
setBackgroundImage(_:for:), backgroundImage(for:)UIImage?, UIControl.StateФон (растягивается по contentEdgeInsets).
setAttributedTitle(_:for:)NSAttributedString?Форматированный заголовок.
titleEdgeInsets, imageEdgeInsets, contentEdgeInsetsUIEdgeInsetsОтступы. contentEdgeInsets сдвигает всю «контент-область».
titleLabel, imageViewUILabel?, UIImageView?Прямой доступ к подвьюхам (не рекомендуется менять constraints).

Современный API (iOS 15+, button.configuration = UIButton.Configuration...)

КонфигурацияОписание
plain()Простая кнопка (как UIButton(type: .system)).
tinted()С заполнением и тенью (акцентная).
filled()Сплошная заливка (по умолчанию с title, image).
bordered(), borderedProminent(), borderedTinted()С рамкой.
gray(), destructive()Предустановленные стили.
Свойства UIButton.ConfigurationТипОписание
title, attributedTitleString?, NSAttributedString?
image, alternativeImageUIImage?alternativeImage — при .selected или .highlighted.
baseBackgroundColor, baseForegroundColorUIColor?Базовые цвета (адаптивные под тему).
backgroundUIButton.Configuration.Background.filled, .tinted, .bordered, кастомный градиент.
cornerStyleUIButton.Configuration.CornerStyle.dynamic, .fixed(CGFloat), .capsule, .medium, .large.
buttonSizeUIButton.Configuration.Size.mini, .small, .medium, .large.
titleAlignment, imagePlacementUIControl.ContentHorizontalAlignment, NSDirectionalRectEdgeПозиционирование.
showsActivityIndicatorBoolПоказывать UIActivityIndicatorView вместо image (например, при загрузке).
subtitle, attributedSubtitleString?, NSAttributedString?Дополнительный текст (под заголовком).
contentInsetsNSDirectionalEdgeInsetsОтступы (directional).

Преимущества Configuration API:

  • Автоматическая поддержка Dynamic Type, Dark Mode, Reduce Motion.
  • Нет необходимости управлять состояниями вручную — система сама переключает title/image/background.
  • Поддержка showsActivityIndicator без кастомной логики.

3.6. UISwitch

СвойствоТипОписание
isOnBoolСостояние.
onTintColor, thumbTintColorUIColor?Цвет фона (включено) и «ползунка».
onImage, offImageUIImage?Иконки внутри ползунка (редко используются).
setEnabled(_:animated:)funcАнимированное изменение состояния disabled.

⚠️ iOS 14+ игнорирует onTintColor, если включён Accessibility → Increase Contrast — используется системный цвет.


3.7. UISlider

СвойствоТипОписание
valueFloatТекущее значение.
minimumValue, maximumValueFloatДиапазон.
minimumValueImage, maximumValueImageUIImage?Иконки на концах.
minimumTrackTintColor, maximumTrackTintColor, thumbTintColorUIColor?Цвета треков и ползунка.
isContinuousBoolОтправлять valueChanged при каждом движении (true) или только по окончании (false).
setThumbImage(_:for:)funcКастомный thumb для состояния.
minimumTrackImage, maximumTrackImageUIImage?Замена track’ов на изображения.

3.8. UIProgressView

СвойствоТипОписание
progressFloat0.01.0.
progressViewStyleUIProgressView.Style.default, .bar.
progressTintColor, trackTintColorUIColor?Цвет заполнения и фона.
progressImage, trackImageUIImage?Кастомные изображения (редко).
setProgress(_:animated:)funcАнимированное обновление.

3.9. UIActivityIndicatorView

СвойствоТипОписание
styleUIActivityIndicatorView.Style.medium, .large, .white, .gray, .whiteLarge (устаревшие), .medium/.large — preferred.
colorUIColor?Цвет индикатора (переопределяет стиль).
hidesWhenStoppedBoolАвтоскрытие при stopAnimating().
startAnimating(), stopAnimating()funcУправление.
isAnimatingBoolСостояние.

✅ iOS 13+: UIActivityIndicatorView автоматически адаптирует цвет под тему, если color не задан.


3.10. UIStackView

СвойствоТипОписание
axisNSLayoutConstraint.Axis.horizontal, .vertical.
distributionUIStackView.Distribution.fill, .fillEqually, .fillProportionally, .equalSpacing, .equalCentering.
alignmentUIStackView.Alignment.fill, .leading, .trailing, .center, .firstBaseline, .lastBaseline.
spacingCGFloatРасстояние между arrangedSubviews.
arrangedSubviews[UIView]Дочерние вьюхи, управляемые stack’ом.
isLayoutMarginsRelativeArrangementBoolУчитывать layoutMargins при расчёте позиций.
addArrangedSubview(_:), removeArrangedSubview(_:)funcДобавление/удаление (не удаляет из иерархии — только из layout’а).

⚠️ Важно:

  • UIStackView не управляет размерами arrangedSubviews — он только распределяет свободное пространство.
  • При distribution = .fillEqually — все вьюхи получают одинаковый length (width при horizontal, height при vertical).
  • При spacing = 0 и отсутствии constraints — возможны наложения.

3.11. Accessibility Attributes (универсальные для UIAccessibility)

Свойство (через view.accessibilityX)ТипОписание
accessibilityLabelString?Краткое описание элемента («Кнопка отправки», «Имя пользователя»).
accessibilityHintString?Дополнительное пояснение («Дважды нажмите для отправки»).
accessibilityValueString?Текущее значение («50%», «3 из 10»).
accessibilityTraitsUIAccessibilityTraitsБитовая маска: .button, .link, .searchField, .image, .selected, .playsSound, .keyboardKey, .staticText, .summaryElement, .updatesFrequently, .startsMediaSession, .adjustable, .allowsDirectInteraction, .causesPageTurn, .tabBar.
accessibilityFrameCGRectПозиция в screen coordinates (переопределяет frame для VoiceOver).
accessibilityFrameInContainerSpaceCGRectТо же, но в координатах контейнера (лучше для rotation).
accessibilityLanguageString?Язык содержимого (например, "ru-RU"), если отличается от локали приложения.
accessibilityElements[Any]?Кастомный порядок обхода (для контейнеров).
accessibilityContainerTypeUIAccessibilityContainerType.automatic, .semanticGroup, ..landmark, ..list, ..row, ..tree.
accessibilityRespondsToUserInteractionBoolЭлемент реагирует на касания (например, UILabel с isUserInteractionEnabled = true).
accessibilityIgnoresInvertColorsBoolНе инвертировать при Smart Invert.
accessibilityViewIsModalBoolОграничивать VoiceOver текущим окном (например, для alert’ов).
shouldGroupAccessibilityChildrenBoolГруппировать дочерние элементы в один узел (например, для ячеек таблицы).

Проверка:

  • Используйте Accessibility Inspector в Xcode (Xcode → Open Developer Tool → Accessibility Inspector).
  • UIAccessibility.post(notification:argument:) — для announce’а изменений (например, UIAccessibility.Notification.layoutChanged).

3.12. Core Animation Layer Properties (через view.layer)

СвойствоТипОписание
cornerRadiusCGFloatСкругление углов. При masksToBounds = true — обрезка содержимого.
borderWidth, borderColorCGFloat, CGColor?Рамка.
shadowColor, shadowOpacity, shadowOffset, shadowRadiusCGColor?, Float, CGSize, CGFloatТень (hardware-accelerated, если shouldRasterize = false).
masksToBoundsBoolЭквивалент clipsToBounds.
shouldRasterizeBoolКэшировать layer как bitmap (ускоряет сложные иерархии, но увеличивает память и может вызывать artefact’ы при масштабировании).
rasterizationScaleCGFloatДолжен быть = UIScreen.main.scale при shouldRasterize = true.
drawsAsynchronouslyBoolРендеринг draw(_:) в фоновом потоке (только для CALayerDelegate).
contentsAny?Прямой bitmap (редко — обычно через UIImage).
contentsGravityCALayerContentsGravityПоведение contents при изменении bounds (аналог contentMode).
zPositionCGFloatZ-смещение (влияет на порядок отрисовки внутри одного superview).
anchorPointCGPointТочка привязки transform’ов и позиционирования (по умолчанию {0.5, 0.5}).
anchorPointZCGFloatZ-компонента anchor point’а.
sublayerTransformCATransform3DTransform для всех sublayers.

⚠️ Производительность:

  • Тень + masksToBounds = trueoffscreen rendering (дорого!). Решение: отдельный shadow-layer без masksToBounds.
  • shouldRasterize = true + анимация transform/alpha → перерастеризация кадр за кадром (avoid!).

📘 Модальные представления, Scene Lifecycle, Фоновые режимы, Privacy & Permissions

Фокус — на публичных API, runtime-состояниях, диагностируемых атрибутах и поведениях, критичных для корректной работы приложения в реальных условиях (включая edge cases: отклонение разрешений, переходы между foreground/background, adaptive interfaces).


4.1. Модальные представления: UIPresentationController, UISheetPresentationController (iOS 15+), UIAdaptivePresentationController

Начиная с iOS 13, модальный показ управляется через presentation controller, а не напрямую через UIViewController.
Конфигурация задаётся через modalPresentationStyle, modalTransitionStyle, и (для sheet’ов) — через sheetPresentationController.

4.1.1. Основные UIModalPresentationStyle и их поведение

ЗначениеПоддержкаПоведениеОсобенности
.fullScreeniOS 2+Занимает весь экран, заменяет rootViewController визуально.Принудительно затемняет background. Не анимируется снизу — всплывает/исчезает.
.pageSheetiOS 13+Лист «страницы»: на iPhone — почти full screen, на iPad — центрированная панель с фоном.Ширина: ~540pt на iPad, ~full на iPhone. Поддерживает pull-to-dismiss.
.formSheetiOS 3.2+Центрированная форма (устарела на iPhone — ведёт себя как .pageSheet).На iPad: фиксированный размер (~540×620 pt).
.overFullScreeniOS 3+Поверх всего, без затемнения фона.Используется для прозрачных оверлеев (например, кастомные alert’ы). Требует modalTransitionStyle = .crossDissolve для плавности.
.overCurrentContextiOS 7+Поверх текущего VC (не root), с затемнением.Background VC остаётся в viewDidAppear, но неактивен.
.popoveriPad-onlyВсплывающее окно с anchor.Требует popoverPresentationController?.sourceView и sourceRect. При tap outside — автоматически dismiss (если popoverPresentationController?.passthroughViews == nil).
.automaticiOS 13+Автовыбор: на iPhone → .fullScreen, на iPad + compact height → .pageSheet, иначе — .formSheet.Рекомендуемое значение по умолчанию.

4.1.2. UISheetPresentationController (iOS 15+)

Появляется, когда modalPresentationStyle = .pageSheet и VC поддерживает adaptive sheet (по умолчанию — да).

СвойствоТипОписание
isPrefersGrabberVisibleBoolПоказывать «ручку» сверху sheet’а (по умолчанию false).
largestUndimmedDetentIdentifierUISheetPresentationController.Detent.Identifier?До какого detent’а не затемнять фон: .medium, .large, .all, nil.
detents[UISheetPresentationController.Detent]Возможные высоты:
  • .medium() — ~½ экрана
  • .large() — почти full height
  • .custom(resolver:) — кастомная высота в точках |
    | selectedDetentIdentifier | UISheetPresentationController.Detent.Identifier? | Текущая высота (read-only). | | prefersScrollingExpandsWhenScrolledToEdge | Bool | Sheet автоматически раскрывается при скролле вверх (например, как в Mail). | | prefersEdgeAttachedInCompactHeight | Bool | На iPhone в landscape — прикреплять к краю, а не центрировать. | | widthFollowsPreferredContentSizeWhenEdgeAttached | Bool | При prefersEdgeAttachedInCompactHeight = true — ширина = preferredContentSize.width. |

✅ Пример адаптивного sheet’а:

if let sheet = vc.sheetPresentationController {
sheet.detents = [.medium(), .large()]
sheet.largestUndimmedDetentIdentifier = .medium
sheet.isPrefersGrabberVisible = true
}

4.1.3. UIAdaptivePresentationController и адаптация

Контроллер, управляющий изменением presentation style при смене traits (например, iPad → split view).

СвойствоТипОписание
delegateUIAdaptivePresentationControllerDelegate?
  • adaptivePresentationStyle(for:) — вернуть новый стиль при изменении traits
  • presentationController(_:viewControllerAdaptivePresentationStyleFor:) — то же, но per-VC
  • presentationController(_:willPresentWithAdaptiveStyle:transitionCoordinator:) — перед адаптацией |
    | overrideTraitCollection | UITraitCollection? | Принудительно задать traits для presented VC (например, .init(horizontalSizeClass: .compact) для iPad sheet’а). |

⚠️ Если не реализовать делегат — при повороте iPad sheet может неожиданно стать .fullScreen.


4.2. Scene Lifecycle (UIScene, UIWindowScene, UISceneDelegate)

С iOS 13+ приложение может иметь несколько сцен (multiple windows на iPad, external display, Handoff, Siri Shortcuts).

4.2.1. Ключевые объекты

ОбъектРоль
UISceneConfigurationОписывает сцену в Info.plistUIApplicationSceneManifest. Имя, role (.windowApplication, .windowExternalDisplay), storyboard/class.
UISceneSessionRuntime-сессия сцены: identifier: String, role: UIScene.Role, configuration: UISceneConfiguration, state: UIScene.State.
UISceneАбстрактный класс. Реализации: UIWindowScene, UIWindowlessScene (редко).
UIWindowSceneСцена с окном. Имеет windows: [UIWindow], screen: UIScreen, activationState: UIScene.ActivationState.
UISceneDelegateАналог UIApplicationDelegate, но для сцены.

4.2.2. UIScene.ActivationState

ЗначениеОписаниеПримечание
.unattachedСцена создана, но не привязана к экрану.После scene(_:willConnectTo:options:), до sceneDidDisconnect(_:).
.foregroundInactiveНа переднем плане, но неактивна (например, Control Center открыт).Аналог UIApplication.State.inactive.
.foregroundActiveПолностью активна, получает события.Аналог UIApplication.State.active.
.backgroundВ фоне.Аналог UIApplication.State.background.
.discardedнет такого значения. Сцена уничтожается через sceneDidDisconnect(_:).

4.2.3. События UISceneDelegate

МетодКогда вызываетсяАналог в UIApplicationDelegate
scene(_:willConnectTo:options:)При создании сценыapplication(_:willFinishLaunchingWithOptions:) + часть didFinishLaunching
sceneDidDisconnect(_:)При уничтожении сценыapplicationWillTerminate(_:) (но не всегда — сцена может быть background’ом)
sceneDidBecomeActive(_:).foregroundActiveapplicationDidBecomeActive(_:)
sceneWillResignActive(_:).foregroundInactiveapplicationWillResignActive(_:)
sceneDidEnterBackground(_:).backgroundapplicationDidEnterBackground(_:)
sceneWillEnterForeground(_:).foregroundInactiveapplicationWillEnterForeground(_:)
scene(_:openURLContexts:)При открытии URLapplication(_:open:options:)
scene(_:continue:)Handoffapplication(_:continue:restorationHandler:)
scene(_:didUpdate:)Изменение traits (size class, scale и др.)traitCollectionDidChange(_:) у корневого VC

⚠️ Важно:

  • application(_:didFinishLaunchingWithOptions:) всё ещё вызывается, но не должен создавать UI — только инициализировать глобальное состояние.
  • Все window’ы создаются в scene(_:willConnectTo:options:), а не в AppDelegate.

4.3. Фоновые режимы: UIBackgroundModes и соответствующие API

Система разрешает ограниченное выполнение в фоне только при наличии entitlement’ов, объяснения в App Store Connect и корректного использования API.

4.3.1. Поддерживаемые режимы и требования

UIBackgroundModesEntitlementОбязательное APIОграниченияМакс. время
audiocom.apple.developer.audio.backgroundAVAudioSession.setCategory(.playback, options: .mixWithOthers) + play()Должен проигрывать или записывать аудио.∞ (пока аудио идёт)
locationcom.apple.developer.location.backgroundCLLocationManager.startUpdatingLocation() или startMonitoringSignificantLocationChanges()Точность ≤ kCLLocationAccuracyHundredMeters для significant.∞ (но с пониженной частотой)
fetchUIApplication.shared.setMinimumBackgroundFetchInterval(_:) и реализация application(_:performFetchWithCompletionHandler:)Запускается по эвристике (раз в несколько часов).~30 сек
remote-notificationremote-notification в APScontent-available: 1 в payload + реализация application(_:didReceiveRemoteNotification:fetchCompletionHandler:)Не гарантируется доставка; не для interactive notifications.~30 сек
bluetooth-centralcom.apple.developer.bluetooth-centralCBCentralManager с CBCentralManagerOptionRestoreIdentifierKeyДолжен подключаться/сканировать устройства.∞ (при подключении)
bluetooth-peripheralcom.apple.developer.bluetooth-peripheralCBPeripheralManager с restore identifierДолжен рекламировать сервисы.∞ (при подключении)
background-processingcom.apple.developer.background-modes + BGTaskSchedulerBGProcessingTaskRequest, BGAppRefreshTaskRequest (iOS 13+)Требует registration в application(_:didFinishLaunchingWithOptions:).До 30 мин (processing), ~10 сек (refresh)
voipУдалено в iOS 8+Недоступно. Используйте PushKit + CallKit.
continuous-waveform-processingcom.apple.developer.hearable.capabilities.raw-streamingHAAudioStream (Hearing Aid API)Только для устройств поддержки слуха.
app-eventscom.apple.developer.device-information.user-assigned-device-nameNSUserActivity с requiredUserInfoKeysДля Shortcuts/Intents.∞ (но execution — по запросу)

4.3.2. Явные фоновые задачи (beginBackgroundTask)

var bgTask: UIBackgroundTaskIdentifier = .invalid

func startWork() {
bgTask = UIApplication.shared.beginBackgroundTask {
// Expiration handler — вызывается за ~5 сек до таймаута
endWork()
}

// Запустить долгую операцию (например, upload)
uploadData {
endWork()
}
}

func endWork() {
UIApplication.shared.endBackgroundTask(bgTask)
bgTask = .invalid
}
ПараметрЗначение
Макс. время~30 секунд (гарантировано), до ~180 сек в редких случаях (iOS 13+)
ТаймерUIApplication.shared.backgroundTimeRemaining (в секундах, Double)
ОтладкаВ Xcode: Debug → Simulate Background Fetch, Debug → Trigger Background Task Expiration

⚠️ Не используйте beginBackgroundTask для замены fetch/processing — App Review отклонит.

4.3.3. BGTaskScheduler (iOS 13+, рекомендуемо вместо fetch)

Регистрация в application(_:didFinishLaunchingWithOptions:):

BGTaskScheduler.shared.register(
forTaskWithIdentifier: "com.example.refresh",
using: nil
) { task in
// Handle refresh task
self.handleAppRefresh(task as! BGAppRefreshTask)
}

Запуск:

let request = BGAppRefreshTaskRequest(identifier: "com.example.refresh")
request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
try? BGTaskScheduler.shared.submit(request)
Тип задачиМин. интервалИспользование
BGAppRefreshTask15 минутОбновление контента (аналог fetch)
BGProcessingTask1 день (эвристически)Тяжелые операции (очистка кэша, sync)

✅ Преимущества:

  • Система сама оптимизирует энергопотребление.
  • Поддержка requiresNetworkConnectivity, requiresExternalPower.

4.4. Privacy & Permissions: Состояния и диагностика

С iOS 14+ многие разрешения имеют ограниченный доступ (.limited) или временный (.provisional).

4.4.1. Общая схема проверки

switch status {
case .notDetermined:
manager.requestAuthorization()
case .restricted:
// Запрещено на уровне системы (Screen Time, MDM)
case .denied:
// Пользователь отказал — показать rationale и вести в Settings
case .authorized:
// OK
case .limited: // iOS 14+, Photos
// Только выбранные медиа
case .provisional: // iOS 12+, User Notifications
// Без UI-запроса (silent push, Siri)
}

4.4.2. Конкретные менеджеры и их статусы

APIСтатусы (CLAuthorizationStatus, PHAuthorizationStatus и др.)Особенности
CLLocationManager.authorizationStatus.notDetermined, .restricted, .denied, .authorizedAlways, .authorizedWhenInUse, .authorized (iOS 14+, deprecated alias)При .authorizedWhenInUse + запрос на .always — второй запрос не покажется, пока пользователь не перейдёт в Settings.
PHPhotoLibrary.authorizationStatus(for: .readWrite).notDetermined, .restricted, .denied, .authorized, .limited.limited → использовать PHPickerViewController (не UIImagePickerController).
AVCaptureDevice.authorizationStatus(for: .video).notDetermined, .restricted, .denied, .authorizedОтдельно для .microphone.
CTCallCenter / CXCallObserverНе требует entitlement’ов, но данные ограничены (только incoming/outgoing state, без номеров).
UserNotifications (UNUserNotificationCenter.current().getNotificationSettings).notDetermined, .denied, .authorized, .provisional, .ephemeral.provisional — разрешение без запроса (только для non-interruptive уведомлений). .ephemeral — для Siri/Shortcuts (временные).
Bluetooth (CBCentralManager.authorization).notDetermined, .restricted, .denied, .allowedAlwaysПроверяется через CBManager.authorization.
HealthKit (HKHealthStore.authorizationStatus(for:)).notDetermined, .sharingDenied, .sharingAuthorizedОтдельно для read/write per-типу (.quantityType(forIdentifier: .stepCount)).
Speech (SFSpeechRecognizer.authorizationStatus).notDetermined, .restricted, .denied, .authorizedТребует NSSpeechRecognitionUsageDescription.
NFC (NFCNDEFReaderSession.readingAvailable)readingAvailable == false → нет железа или отключено в Settings.

4.4.3. Диагностика через Settings URL

if status == .denied {
if let url = URL(string: UIApplication.openSettingsURLString) {
UIApplication.shared.open(url)
}
}

⚠️ App Review требует:

  • Не показывать запрос разрешения при первом запуске без контекста.
  • Перед запросом — показать «rationale screen» с объяснением зачем нужно разрешение.
  • При .denied — не спамить запросами, а вести в Settings.

📘 Сетевые операции, геолокация, Core Data, Keychain, App Extensions

Фокус — на публичных, стабильных, диагностируемых свойствах и конфигурациях, критичных для production-приложений:

  • Устойчивость к ошибкам
  • Энергоэффективность
  • Соответствие требованиям App Store Review
  • Возможность мониторинга и отладки

5.1. URLSession: Сессии, задачи, метрики, кэширование

5.1.1. Типы сессий и их назначение

ТипСозданиеИспользованиеОсобенности
defaultURLSession(configuration: .default)Обычные запросы (UI, API)Использует глобальный кэш, куки, credentials.
ephemeralURLSession(configuration: .ephemeral)Приватный режим (инкогнито)Не сохраняет куки, кэш, credentials между сессиями.
backgroundURLSession(configuration: .background(withIdentifier: "id"))Загрузки/выгрузки в фонеДолжна быть сильной ссылкой на URLSessionDelegate. Выживает после termination. Требует entitlement com.apple.developer.networking.background-session.
customURLSessionConfiguration() + настройкаТонкая настройкаМожно задать timeoutIntervalForRequest, httpMaximumConnectionsPerHost, tlsMinimumSupportedProtocolVersion, networkServiceType, и др.

5.1.2. Свойства URLSessionConfiguration

СвойствоТипОписание
httpAdditionalHeaders[String : Any]?Заголовки по умолчанию для всех задач.
timeoutIntervalForRequestTimeIntervalТаймаут на установку соединения + отправку/получение данных (по умолчанию 60 сек).
timeoutIntervalForResourceTimeIntervalМакс. время выполнения задачи (по умолчанию 7 дней — только для background-сессий).
httpMaximumConnectionsPerHostIntМакс. одновременных соединений к одному хосту (по умолчанию 6).
httpCookieAcceptPolicyHTTPCookie.AcceptPolicy.always, .never, .onlyFromMainDocumentDomain
urlCacheURLCache?Кэш (по умолчанию — общий для default-сессии).
requestCachePolicyNSURLRequest.CachePolicy.useProtocolCachePolicy, .reloadIgnoringLocalCacheData, .returnCacheDataElseLoad, .returnCacheDataDontLoad
networkServiceTypeURLSessionTask.NetworkServiceType.default, .voip, .video, .background, .voice, .networkServiceTypeResponsiveData
sharedContainerIdentifierString?Для background-сессий: указывает app group для обмена данными между приложением и extension’ами.
sessionSendsLaunchEventsBoolДля background-сессий: отправлять ли application(_:handleEventsForBackgroundURLSession:completionHandler:) при launch.

5.1.3. URLSessionTask — ключевые свойства

СвойствоТипОписание
taskIdentifierIntУникальный ID задачи в рамках сессии.
originalRequest, currentRequestURLRequest?original — как создана, current — с учётом редиректов.
responseURLResponse?Ответ от сервера (после didReceive).
countOfBytesSent, countOfBytesReceivedInt64Счётчики передачи (актуальны в didSendBodyData, didCompleteWithError).
stateURLSessionTask.State.suspended, .running, .canceling, .completed
priorityFloat0.01.0 (по умолчанию 0.5). Влияет на порядок отправки в httpMaximumConnectionsPerHost.
earliestBeginDateDate?Мин. дата начала (для BGAppRefreshTaskURLSession).
metricsURLSessionTaskMetrics?Только после завершения задачи.

5.1.4. URLSessionTaskMetrics — детализация времени

Доступна в urlSession(_:task:didCompleteWithError:)(task as? URLSessionDataTask)?.transactionMetrics.

СвойствоТипОписание
taskIntervalTimeIntervalПолное время выполнения.
redirectCountIntКоличество редиректов.
transactionMetrics[URLSessionTaskTransactionMetrics]По одному на каждый «хоп» (оригинал + редиректы).

Для каждого URLSessionTaskTransactionMetrics:

СвойствоОписание
fetchStartDateНачало DNS lookup.
domainLookupStartDate, domainLookupEndDateВремя DNS.
connectStartDate, connectEndDateУстановка TCP-соединения.
secureConnectionStartDate, secureConnectionEndDateTLS handshake.
requestStartDateОтправка HTTP-заголовков.
requestEndDateЗавершение отправки тела (если есть).
responseStartDateПолучение первых байт ответа.
responseEndDateЗавершение получения тела.

✅ Пример диагностики медленного запроса:

let dns = metric.domainLookupEndDate.timeIntervalSince(metric.domainLookupStartDate)
let tls = metric.secureConnectionEndDate.timeIntervalSince(metric.secureConnectionStartDate)
let ttfb = metric.responseStartDate.timeIntervalSince(metric.requestEndDate)

5.1.5. Background URLSession — ограничения и требования

  • Делегат должен быть сильной ссылкой (обычно — singleton или retained во AppDelegate).
  • В application(_:handleEventsForBackgroundURLSession:completionHandler:) — сохранить completionHandler, вызвать его после urlSessionDidFinishEvents(forBackgroundURLSession:).
  • Не поддерживает uploadTask(with:from:) с Data — только с FileURL.
  • Не поддерживает интерактивные задачи (streaming, WebSocket).
  • Макс. 100 задач на сессию (иначе — NSURLErrorBackgroundSessionInUseByAnotherProcess).

5.2. CLLocationManager: Точность, энергопотребление, режимы

5.2.1. Свойства и их влияние

СвойствоТипВозможные значения / Примечания
desiredAccuracyCLLocationAccuracy
  • kCLLocationAccuracyBestForNavigation — GPS + motion, высокая точность, высокое энергопотребление (только для foreground)
  • kCLLocationAccuracyBest — GPS, высокая точность
  • kCLLocationAccuracyNearestTenMeters
  • kCLLocationAccuracyHundredMeters — смартфонный GPS (рекомендуется для большинства случаев)
  • kCLLocationAccuracyKilometer, ThreeKilometers — cell/wifi only
  • kCLLocationAccuracyReduced (iOS 14+) — ~200 м, низкое энергопотребление |
    | distanceFilter | CLLocationDistance | Мин. расстояние между update’ами (в метрах). kCLDistanceFilterNone = каждый update. | | activityType | CLActivityType | .other, .automotiveNavigation, .fitness, .otherNavigation | Влияет на фильтрацию шума и энергопотребление. | | pausesLocationUpdatesAutomatically | Bool | Приостанавливать updates, если пользователь неподвижен (по умолчанию true). | | showsBackgroundLocationIndicator | Bool (iOS 15+) | Показывать индикатор в status bar при фоновой геолокации (по умолчанию true). | | allowsBackgroundLocationUpdates | Bool | Разрешить updates в фоне (требует UIBackgroundModes: location). |

5.2.2. Режимы мониторинга

РежимМетодЭнергоэффективностьТочностьИспользование
ContinuousstartUpdatingLocation()НизкаяВысокаяНавигация, фитнес-трекеры
Significant ChangestartMonitoringSignificantLocationChanges()Очень высокая~500 мОбщее местоположение (погода, город)
Region MonitoringstartMonitoring(for:) (CLCircularRegion, CLBeaconRegion)ВысокаяВход/выход из радиусаГеозоны, маяки
Deferred UpdatesallowDeferredLocationUpdates(untilTraveled:timeout:)СредняяКак в continuousПакетная передача точек (например, при записи трека)
Visit MonitoringstartMonitoringVisits()Очень высокаяПрибытие/уход из местаПоведенческий анализ

⚠️ Ограничения:

  • significantLocationChanges не работает в симуляторе.
  • region monitoring поддерживает до 20 регионов одновременно.
  • deferred updates работают только при desiredAccuracy = kCLLocationAccuracyBest и activityType = .fitness.

5.2.3. Диагностика ошибок

Код ошибки (CLError.Code)ПричинаДействие
.deniedПользователь запретилПроверить authorizationStatus, вести в Settings
.networkНет сети/GPSПовторить позже, использовать кэш
.headingFailureНет магнитометраОтключить compass
.rangingUnavailableBluetooth выключенПроверить CBCentralManager.state
.regionMonitoringDenied, .regionMonitoringFailureОграничения системыНе более 20 регионов, не менее 25 м радиус

5.3. Core Data: Контексты, конкурентность, производительность

5.3.1. NSPersistentContainer — стандартная конфигурация

СвойствоТипОписание
nameStringИмя модели данных (.xcdatamodeld).
persistentStoreDescriptions[NSPersistentStoreDescription]Описания хранилищ (SQLite по умолчанию).
viewContextNSManagedObjectContextMain-queue context для UI. Не используйте для записи в фоне.
performBackgroundTask(_:)funcСоздаёт новый private-queue context, выполняет блок, сохраняет, сливает в viewContext.

5.3.2. Типы NSManagedObjectContext

ТипСозданиеПотокИспользование
Main QueueNSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType)Главный потокТолько чтение или UI-обновления.
Private QueueNSManagedObjectContext(concurrencyType: .privateQueueConcurrencyType)Отдельная очередьЗапись, импорт, тяжёлые операции.
Confinement.confinementConcurrencyType (устарело в iOS 10)Любой, но один потокНе рекомендуется.

✅ Правильный паттерн для записи:

container.performBackgroundTask { backgroundContext in
// Выполняем в private queue
let obj = NSEntityDescription.insertNewObject(forEntityName: "User", into: backgroundContext)
obj.name = "Timur"
try? backgroundContext.save() // Автоматически вызывает merge в viewContext
}

5.3.3. Merge Policies

ПолитикаПоведение при конфликте (внешнее изменение vs локальное)
.errorВыбрасывает NSMergeConflict.
.rollbackОткатывает локальные изменения.
.overwriteПерезаписывает внешние изменения локальными.
.mergeByPropertyObjectTrumpПриоритет локального объекта (по свойствам).
.mergeByPropertyStoreTrumpПриоритет данных из хранилища.

5.3.4. Производительность: Faulting, Prefetching, Batching

МеханизмСвойство / МетодЭффект
FaultingNSManagedObject.isFaultЛенивая загрузка атрибутов/relationship’ов.
PrefetchingNSFetchRequest.relationshipKeyPathsForPrefetching = ["author", "tags"]Загрузка связанных объектов одним запросом.
Batch FaultingNSFetchRequest.fetchBatchSize = 20Загружать объекты пакетами (уменьшает peak memory).
LimitfetchRequest.fetchLimit = 100Ограничение выборки.
Asynchronous FetchNSAsynchronousFetchRequestФоновая выборка с прогрессом.
Batch UpdatesNSBatchUpdateRequestОбновление без загрузки объектов в память (UPDATE SQL).

⚠️ Опасные практики:

  • Доступ к NSManagedObject вне perform(_:)CoreData: fault: ... was not updated correctly.
  • Передача NSManagedObjectID вместо объекта между контекстами — безопасно.

5.4. Keychain Services: Доступность, шаринг, биометрия

5.4.1. kSecAttrAccessible — когда данные доступны

ЗначениеДоступность
kSecAttrAccessibleWhenUnlockedТолько после разблокировки (после reboot — недоступно). Рекомендуется по умолчанию.
kSecAttrAccessibleAfterFirstUnlockПосле первого разблокирования (даже в фоне).
kSecAttrAccessibleAlwaysВсегда (включая reboot). Удалено в iOS 9+ (заменено на WhenUnlocked).
...ThisDeviceOnly суффиксНе синхронизируется в iCloud/iTunes.
kSecAttrAccessibleWhenPasscodeSetThisDeviceOnlyТолько если включён пароль + не синхронизируется.

5.4.2. Совместный доступ (App Groups)

ПараметрЗначение
kSecAttrAccessGroup"group.com.example.app" — должен совпадать с entitlement.
Entitlementcom.apple.security.application-groups

5.4.3. Биометрия (LocalAuthentication + Keychain)

let query: [String: Any] = [
kSecClass as String: kSecClassGenericPassword,
kSecAttrAccount as String: "user_token",
kSecAttrService as String: "com.example.app",
kSecUseAuthenticationUI as String: kSecUseAuthenticationUIFail, // Не показывать UI, только проверить
kSecUseAuthenticationContext as String: context // LAContext с биометрией
]
КлючЗначениеЭффект
kSecUseAuthenticationUIkSecUseAuthenticationUIFail, kSecUseAuthenticationUIAllow, kSecUseAuthenticationUISkipПоказывать ли system prompt.
kSecUseOperationPrompt"Подтвердите вход Face ID"Текст в системном диалоге.
kSecAttrTokenIDkSecAttrTokenIDSecureEnclaveХранить в Secure Enclave (только для ключей, не для паролей).

⚠️ Ограничения:

  • Данные с kSecAttrAccessibleWhenPasscodeSet... недоступны, если пароль не установлен.
  • Биометрия работает только на устройствах с Touch ID/Face ID. Проверяйте LAContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error:).

5.5. App Extensions: Типы, Context, Data Sharing

5.5.1. Поддерживаемые типы расширений (iOS)

ТипNSExtensionPointIdentifierОписание
Today Widgetcom.apple.widgetkit-extensionWidgetKit (iOS 14+). Устаревший com.apple.widget-extension (pre-iOS 14).
Sharecom.apple.share-servicesРасшаривание контента.
Actioncom.apple.servicesОбработка выделенного текста/изображения.
Photo Editingcom.apple.photo-editingРедактор в Photos.app.
Document Providercom.apple.fileprovider-nonui / -uiИнтеграция с Files.app.
Custom Keyboardcom.apple.keyboard-serviceКастомная клавиатура.
Intentcom.apple.intents-serviceSiri Shortcuts, Spotlight suggestions.
Intent UIcom.apple.intents-ui-serviceКастомный интерфейс для intent’а.
Notification Contentcom.apple.usernotifications.content-extensionКастомный UI уведомления.
Notification Servicecom.apple.usernotifications.serviceИзменение уведомления перед показом (media, encryption).

5.5.2. NSExtensionContext — ключевые свойства

СвойствоТипОписание
inputItems[NSExtensionItem]Входные данные (текст, URL, изображения).
attachments[NSItemProvider]Файлы/данные через NSItemProvider.
hostProtocolNSXPCConnectionДля кастомного IPC с хост-приложением (редко).
completeRequest(returningItems:completionHandler:)funcЗавершить расширение и вернуть результат.
cancelRequest(withError:)funcОтменить с ошибкой.

5.5.3. Обмен данными с основным приложением

МеханизмОграничения
App Groups (UserDefaults(suiteName:), FileManager.containerURL(forSecurityApplicationGroupIdentifier:))Общий UserDefaults, файлы, Core Data (через shared container).
Keychain SharingЧерез kSecAttrAccessGroup.
URL Schemesopen(_:options:completionHandler:)application(_:open:options:).
Handoff (NSUserActivity)Только для public.app-category данных.
CloudKit / iCloud DriveТребует включения iCloud entitlement’ов.

⚠️ Важно:

  • Расширение не имеет доступа к mainBundle — только к своему bundle.
  • Время выполнения ограничено (~30 сек для большинства, ~20 мс для keyboard).
  • Не используйте UIApplication.shared — он недоступен в extension’ах.

📘 SwiftUI, Accessibility в SwiftUI, WidgetKit, Push Notifications, App Clips

Фокус — на публичных, стабильных API, runtime-диагностике, совместимости, а также на ограничениях и edge cases, критичных для production-развёртывания.


6.1. SwiftUI: Environment, Modifiers, Lifecycle, UIKit Interop

6.1.1. Ключевые @Environment значения (доступны через @Environment(\.key))

КлючТипОписаниеПримечания
\.colorSchemeColorScheme.light, .darkСоответствует UITraitCollection.userInterfaceStyle.
\.sizeClass.horizontal, \.sizeClass.verticalUserInterfaceSizeClass?.compact, .regular, nilДля адаптации под split view/iPad.
\.dynamicTypeSizeDynamicTypeSize.xxxSmall, .accessibilityXXXLargeЗамена UIContentSizeCategory.
\.legibilityWeightLegibilityWeight.regular, .boldДля lock screen/нотификаций.
\.layoutDirectionLayoutDirection.leftToRight, .rightToLeftУправляется через UIView/userInterfaceLayoutDirection.
\.localeLocaleТекущая локаль (из UITraitCollection.preferredLanguages).
\.redactionReasonsRedactionReasons.placeholder (при redacted(reason:))Для skeleton state’ов.
\.isEnabledBoolСостояние disabled (наследуется от родителя).
\.editModeBinding<EditMode>?Режим редактирования (например, EditButton()).
\.horizontalSizeClass, \.verticalSizeClassUserInterfaceSizeClass?То же, что и sizeClass, но deprecated в пользу \.sizeClass.
\.scenePhaseScenePhase.active, .inactive, .backgroundАналог UIApplication.State.
\.dismissDismissAction@Environment(\.dismiss) var dismissdismiss()Для программного закрытия sheet/navigationStack.
\.openURLOpenURLAction@Environment(\.openURL) var openURLopenURL(URL(string:"...")!)Безопасная замена UIApplication.shared.open.
\.colorSchemeContrastColorSchemeContrast.standard, .increased (iOS 17+)Для Accessibility → Increase Contrast.

✅ Пример адаптации под тему:

struct ContentView: View {
@Environment(\.colorScheme) var colorScheme
var body: some View {
Text("Текст")
.foregroundColor(colorScheme == .dark ? .white : .black)
}
}

6.1.2. Жизненный цикл View

ModifierКогда вызываетсяАналог в UIKit
.onAppear { }После вставки в иерархию и первого render’аviewDidAppear(_:)
.onDisappear { }Перед удалением из иерархииviewDidDisappear(_:)
.task { }При появлении + отмена при исчезновении (через Task.cancel()).viewDidAppear + viewDidDisappear с Task
.task(id: value) { }Перезапуск при изменении id
.onChange(of: value) { }При изменении значения (только если view в иерархии)didSet + проверка isViewLoaded
.onReceive(Publisher) { }При получении события от publisher’аNotificationCenter.addObserver

⚠️ Важно:

  • init() у View вызывается много раз — не используйте для side effects.
  • Для долгих операций — только .task или .onReceive с @StateObject.

6.1.3. Согласование SwiftUI и UIKit

СценарийРешение
Встраивание UIKit в SwiftUIUIViewRepresentable / UIViewControllerRepresentable
Доступ к UIKit-состоянию из SwiftUICoordinator в Representable + @Binding
Передача данных в UIKitupdateUIView(_:, context:) вызывается при изменении @State, @Binding, @Environment.
SwiftUI → UIKit navigationИспользовать UIHostingController как root/modal VC.
UIKit → SwiftUI navigationВ UIHostingControllerrootView: some View.
Общий EnvironmentЧерез UIHostingController.rootView = YourView().environment(\.key, value).

✅ Пример UIViewRepresentable с gesture:

struct CustomView: UIViewRepresentable {
@Binding var value: CGFloat

func makeUIView(context: Context) -> UIView {
let view = UIView()
let pan = UIPanGestureRecognizer(target: context.coordinator, action: #selector(Coordinator.panned))
view.addGestureRecognizer(pan)
context.coordinator.view = view
return view
}

func updateUIView(_ uiView: UIView, context: Context) {
// Обновление при изменении @Binding
}

class Coordinator {
var view: UIView?
@Binding var value: CGFloat
init(_ value: Binding<CGFloat>) { self._value = value }
@objc func paned(_ g: UIPanGestureRecognizer) { /* обновить value */ }
}

func makeCoordinator() -> Coordinator { Coordinator($value) }
}

6.2. Accessibility в SwiftUI

ModifierОписаниеПример
.accessibilityLabel("Отправить")Замена текста для VoiceOver.Button("Send").accessibilityLabel("Отправить")
.accessibilityHint("Дважды нажмите для отправки")Подсказка.
.accessibilityValue("50%")Текущее значение (sliders, progress).
.accessibilityAddTraits(.isButton)Добавить trait..accessibilityAddTraits(.isHeader)
.accessibilityRemoveTraits(.isImage)Удалить trait.
.accessibilityHidden(true)Скрыть от VoiceOver.Для decorative views.
.accessibilitySortPriority(1)Порядок обхода (выше — раньше).
.accessibilityRotor("Элементы", entries: items)Кастомный rotor (iOS 16+).Позволяет быстро переключаться между группами.
.accessibilityFocused($isFocused)Отслеживать фокус (например, для scroll-to).ScrollView { TextField(...).accessibilityFocused($focused) }
.accessibilityRepresentation { ... }Замена view на accessibility-дереве (iOS 17+).Для сложных кастомных элементов.

✅ Диагностика:

  • В симуляторе: Settings → Accessibility → VoiceOver + Accessibility Inspector.
  • print(AccessibilityAudit(view)) — статический анализ (требуется import SwiftUIAccessibility).

6.3. WidgetKit: Configuration, Timelines, Families

6.3.1. Поддерживаемые семейства (iOS)

СемействоРазмер (pt)Описание
.systemSmall110×110Одна иконка/текст.
.systemMedium220×110Компактный контент.
.systemLarge220×220Богатый контент (stack, grid).
.accessoryRectangular110×30В строке уведомлений/Lock Screen.
.accessoryInline~110×20Внутри уведомления.
.accessoryCircular68×68Круглая иконка (Lock Screen).

⚠️ Ограничения:

  • Нет интерактивности (только deep link через URLScheme).
  • Нет network requests в getTimeline(...). Только кэш/фоновая загрузка.
  • Макс. 15 виджетов на устройство от одного приложения.

6.3.2. TimelineProvider

МетодОписание
placeholder(in:)Skeleton-состояние (показывается при первом добавлении).
getSnapshot(...) → EntryPreview в редакторе виджетов.
getTimeline(...) → Timeline<Entry>Реальные данные. Timeline(entries: [...], policy: .atEnd)
TimelineReloadPolicyПоведение
.atEndОбновить после последнего entry.
.after(Date)Обновить после указанного времени.
.neverТолько вручную (через WidgetCenter.shared.reloadTimelines).

6.3.3. Deep Linking из виджета

Link(destination: URL(string: "myapp://dashboard?widget=1")!) {
Text("Открыть")
}

✅ Требует регистрации схемы в Info.plist (CFBundleURLTypes).
✅ В AppDelegate/SceneDelegate — обработка через open(_:options:).


6.4. Push Notifications: APNs, Payload, Critical Alerts

6.4.1. Обязательные поля payload (JSON)

{
"aps": {
"alert": {
"title": "Заголовок",
"body": "Текст",
"subtitle": "Подзаголовок"
},
"badge": 1,
"sound": "default",
"thread-id": "group1",
"category": "MESSAGE_CATEGORY"
},
"custom_key": "value"
}
ПолеТипОписание
alertstring или dictЕсли string — только body.
badgeIntЧисло на иконке. 0 — скрыть.
soundstring"default" или имя .caf файла в bundle.
content-available1Silent push (фоновое обновление).
mutable-content1Разрешить Notification Service Extension модифицировать.
categorystringДля action buttons (регистрируется через UNNotificationCategory).

6.4.2. Critical Alerts (требуют entitlement com.apple.developer.usernotifications.critical-alert)

Дополнительные поляОписание
"sound": { "critical": 1, "name": "alert.caf", "volume": 1.0 }Принудительное воспроизведение даже при silent mode.
"interruption-level": "critical"iOS 15+: вместо critical:1 в sound.

⚠️ Critical alerts требуют одобрения Apple в App Review с justification.

6.4.3. Communication Notifications (iOS 15+, com.apple.developer.usernotifications.communication)

ПолеЗначение
"interruption-level": "time-sensitive"Высокий приоритет (но не critical). Показывается поверх экрана блокировки.
"relevance-score": 0.8Релевантность (0.0–1.0) для сортировки.

✅ Используется для звонков, сообщений, экстренных уведомлений.


6.5. App Clips: Discovery, Invocation, Handoff

6.5.1. Способы запуска

ТриггерТребования
NFC tag#appclip в NDEF record.
QR codeURL с ?clip_id=... + AASA-файл.
Safari Smart Banner<meta name="apple-itunes-app" content="app-clip-id=...">
Messages link previewURL с поддержкой App Clip.
Map pinВ Apple Maps → бизнес с App Clip.

6.5.2. Ограничения

ПараметрЗначение
Макс. размер15 МБ (скачивается за < 2 сек).
Время жизниДо 30 дней (если не установлено полное приложение).
ДанныеОбщие с основным приложением через App Groups/Keychain.
HandoffNSUserActivity с supportsAppClipapplication(_:continue:restorationHandler:).

6.5.3. Диагностика

  • В Xcode: Window → Devices and Simulators → App Clips — просмотр установленных.
  • AppClip.isRunningInAppClip() — runtime-проверка.
  • AppClip.requestEphemeralUserNotification(...) — показ временного уведомления (до 4 часов).

📘 Security, Testing, Localization, Distribution, Debugging

Фокус — на инструментах и атрибутах, критичных для production-стабильности, прохождения App Review, локализации и отладки на уровне инженерной диагностики.


7.1. Security: ATS, Certificate Pinning, SSL, Biometry, Secure Enclave

7.1.1. App Transport Security (ATS)

Конфигурация в Info.plistNSAppTransportSecurity.

КлючТипОписаниеРекомендация
NSAllowsArbitraryLoadsBoolОтключает ATS глобально.Не использовать — отклонят в App Review без justification.
NSExceptionDomainsdictИсключения для доменов.✅ Использовать только для legacy API.
example.comdict
— — NSExceptionAllowsInsecureHTTPLoadsBoolРазрешить HTTP.Только если нет HTTPS.
— — NSIncludesSubdomainsBoolПрименить к поддоменам.
— — NSExceptionMinimumTLSVersionStringTLSv1.1, TLSv1.2, TLSv1.3Минимум — TLSv1.2.
— — NSExceptionRequiresForwardSecrecyBoolТребовать PFS (ECDHE).По умолчанию true.
— — NSRequiresCertificateTransparencyBoolТребовать CT logs.Рекомендуется для финансовых сервисов.

✅ Пример безопасного исключения:

<key>NSExceptionDomains</key>
<dict>
<key>legacy-api.example.com</key>
<dict>
<key>NSExceptionAllowsInsecureHTTPLoads</key>
<false/>
<key>NSExceptionMinimumTLSVersion</key>
<string>TLSv1.2</string>
<key>NSExceptionRequiresForwardSecrecy</key>
<true/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>

7.1.2. Certificate Pinning

Реализуется через URLSessionDelegateurlSession(_:task:didReceive:completionHandler:).

ПодходОписаниеРиски
Public Key PinningСравнение SHA-256 от SecKeyCopyExternalRepresentation.Устойчив к смене сертификата при том же ключе.
Certificate PinningСравнение SHA-256 от DER-представления сертификата.Требует обновления при ротации сертификата.
Backup PinsХранить ≥2 пина (основной + запасной).Избегает downtime при ротации.

⚠️ Важно:

  • Не пинить корневой CA — только leaf/middle.
  • Обеспечить механизм отката (например, через remote config).
  • Использовать SecTrustEvaluateWithError (iOS 12+) вместо SecTrustEvaluate.

7.1.3. LocalAuthentication: Биометрия и аутентификация

PolicyОписание
.deviceOwnerAuthenticationFace ID / Touch ID или пароль.
.deviceOwnerAuthenticationWithBiometricsТолько биометрия.
.deviceOwnerAuthenticationWithWatchFace ID + Apple Watch (iOS 16.4+).
Ключи контекста (LAContext)ТипОписание
localizedReasonStringОбязательно: «Для входа в приложение».
localizedCancelTitleStringКнопка отмены.
touchIDAuthenticationAllowableReuseDurationTimeIntervalПереиспользование Touch ID (устарело).
interactionNotAllowedBoolНе показывать UI (только проверить).

✅ Диагностика:

let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthentication, error: &error) {
context.evaluatePolicy(.deviceOwnerAuthentication) { success, error in
// Обработка
}
} else {
// Биометрия недоступна (нет датчика, отключена)
}

7.1.4. Secure Enclave и Keychain

АтрибутЗначениеЭффект
kSecAttrTokenIDkSecAttrTokenIDSecureEnclaveКлюч генерируется и хранится только в Secure Enclave.
kSecAttrAccessiblekSecAttrAccessibleWhenPasscodeSetThisDeviceOnlyДоступ только при установленном пароле.
kSecUseAuthenticationUIkSecUseAuthenticationUIFailПроверка без UI.
SecAccessControlCreateFlags.biometryCurrentSetКлюч недействителен после сброса биометрии.

✅ Генерация ключа в Secure Enclave:

let attributes: [String: Any] = [
kSecAttrKeyType as String: kSecAttrKeyTypeECSECPrimeRandom,
kSecAttrKeySizeInBits as String: 256,
kSecAttrTokenID as String: kSecAttrTokenIDSecureEnclave,
kSecPrivateKeyAttrs as String: [
kSecAttrIsPermanent as String: true,
kSecAttrApplicationTag as String: "com.example.key"
]
]
let key = SecKeyCreateRandomKey(attributes as CFDictionary, &error)

7.2. Testing: XCTest, UI Testing, Performance, XCTestPlans

7.2.1. XCTestCase: Key Properties & Methods

Свойство/МетодОписание
continueAfterFailure = falseОстановка теста при первом XCTAssert fail.
measure { }Измерение времени выполнения (среднее из 10 итераций).
XCTExpectation + waitForExpectations(timeout:handler:)Асинхронные тесты.
XCTActivityГруппировка шагов в отчёте.
addUIInterruptionMonitor(withDescription:handler:)Обработка системных alert’ов (например, permission).

7.2.2. UI Testing: XCUIElement Queries

QueryОписание
app.buttons["submit"]По accessibility identifier.
app.textFields.matching(identifier: "email").firstMatchФильтрация.
app.descendants(matching: .textField)Все текстовые поля.
element.coordinate(withNormalizedOffset: CGVector(dx: 0.5, dy: 0.5)).tap()Тап в центр элемента.

⚠️ Best practices:

  • Использовать accessibilityIdentifier, а не label.
  • Не полагаться на текст/позицию — только на идентификаторы.
  • sleep() → заменить на XCTWaiter.wait(for:timeout:).

7.2.3. Performance Testing

ИнструментИспользование
Time ProfilerCPU-bound bottlenecks (viewDidLoad, cellForRowAt).
AllocationsУтечки памяти, retain cycles.
LeaksАвтоматическое обнаружение утечек.
Core Data«Core Data Fetches», «Core Data Saves».
Energy LogРейтинг энергоэффективности (App Store требует ≥3/5).

✅ Запуск из кода:

measure(metrics: [XCTClockMetric(), XCTCPUMetric(), XCTMemoryMetric()]) {
heavyOperation()
}

7.2.4. XCTestPlan (Xcode 11+)

ВозможностьОписание
Multiple configurationsРазные DEBUG/RELEASE, языки, devices.
Test selectionВключение/исключение тестов по тегам (#XCTTag("smoke")).
Environment variablesXCInjectBundle, XCTestConfiguration.
Parallel testingЗапуск на нескольких симуляторах одновременно.

7.3. Localization: .xcstrings, Pluralization, Pseudolocalization

7.3.1. Формат .xcstrings (Xcode 15+)

{
"strings": {
"welcome_message": {
"extractionState": "manual",
"localizations": {
"ru": {
"stringUnit": {
"state": "translated",
"value": "Добро пожаловать, %@"
}
},
"en": {
"stringUnit": {
"state": "translated",
"value": "Welcome, %@"
}
}
}
}
}
}
Преимущество перед .stringsОписание
Валидация форматовXcode проверяет %@, %d на этапе сборки.
Поддержка plural/genderВстроенно (см. ниже).
Integration с Xcode CloudАвтоматическая синхронизация.

7.3.2. Pluralization (.stringsdict или .xcstrings)

<!-- Localizable.stringsdict -->
<dict>
<key>files_count</key>
<dict>
<key>NSStringLocalizedFormatKey</key>
<string>%#@files@</string>
<key>files</key>
<dict>
<key>NSStringFormatSpecTypeKey</key>
<string>NSStringPluralRuleType</string>
<key>NSStringFormatValueTypeKey</key>
<string>ld</string>
<key>zero</key>
<string>Нет файлов</string>
<key>one</key>
<string>%ld файл</string>
<key>few</key>
<string>%ld файла</string>
<key>many</key>
<string>%ld файлов</string>
<key>other</key>
<string>%ld файлов</string>
</dict>
</dict>
</dict>

✅ Использование:

String(format: NSLocalizedString("files_count", comment: ""), filesCount)

7.3.3. Pseudolocalization (для тестирования layout)

В Info.plistCFBundleDevelopmentRegion = en, и добавить язык:

en-XA

Xcode автоматически:

  • Удлиняет строки на 40%
  • Добавляет [!! и !!] по краям
  • Заменяет латиницу на акцентированные символы («Ţḗşţ»)

✅ Включается в scheme → Options → Application Language → Pseudolanguage.


7.4. Distribution: App Store Connect API, Notarization, Thinning

7.4.1. App Store Connect API

ЭндпоинтИспользование
POST /v1/appsСоздание нового приложения.
GET /v1/builds?filter[app]=...Список сборок.
PATCH /v1/builds/{id}Изменение статуса («Ready for Sale»).
POST /v1/betaGroups/{id}/buildsДобавление сборки в TestFlight.

✅ Требуется JWT-токен с private key (.p8) и scopes:
app_management, build_upload, beta_tester_invites.

7.4.2. Notarization (для macOS, но затрагивает iOS при Universal)

ШагКоманда
Архивацияxcodebuild -archivePath ... archive
Экспортxcodebuild -exportArchive -exportOptionsPlist export.plist
Отправкаxcrun notarytool submit ... --wait
Staplingxcrun stapler staple MyApp.app

⚠️ Для iOS notarization не требуется, только для macOS/iPadOS (Catalyst).

7.4.3. App Thinning & Slicing

МеханизмОписание
BitcodeПромежуточный код для re-optimization Apple’ом. Устарел в Xcode 14+ (но поддерживается).
On-Demand ResourcesЗагрузка asset’ов по тегам (NSBundleResourceRequest).
Asset Catalog Slicing@2x, @3x, universal → только нужные ресурсы в IPA.
Architecture SlicingТолько arm64 (удалён armv7, i386, x86_64).

✅ Проверка содержимого IPA:

unzip App.ipa
lipo -info Payload/*.app/*.app/Executable

7.5. Debugging: LLDB, View Debugger, Memory Graph

7.5.1. Полезные LLDB команды

КомандаОписание
po viewPrint object (вызывает debugDescription).
v view.frameПечать свойства без вызова методов.
expr view.backgroundColor = UIColor.redИзменение свойства в runtime.
fr vПеременные текущего фрейма.
btBacktrace.
image lookup -rn "ClassName"Поиск символов по regex.
memory read -c 16 -f x view.layerЧтение памяти (hex).

7.5.2. View Debugger (Xcode)

ФичаГорячая клавиша
Debug View Hierarchy⌃+⇧+⌥+C
Показать constraintsГалочка внизу.
Поиск по accessibility labelВ поиске — accessibilityLabel:"Submit".

7.5.3. Memory Graph Debugger

АнализИспользование
Leaked BlocksУтечки с retain count > 0 и нет сильных ссылок из root.
Cycle DetectionГраф с циклами → клик по узлу → «Cycle».
Filter by TypeMyViewController, __NSCFLocalDataTask.

✅ Совет:

  • Запускать на реальном устройстве — симулятор маскирует некоторые утечки.
  • Использовать DeinitLogger:
    deinit { print("Deinit \(Self.self)") }

📘 Производительность, Crash Reporting, CI/CD, Документация, Deprecation

Фокус — на диагностике, мониторинге, автоматизации и поддержке кодовой базы в долгосрочной перспективе, с учётом требований к качеству, воспроизводимости и сопровождаемости.


8.1. Производительность: FPS, Rendering, Память, Энергия

8.1.1. Измерение FPS и кадровых провалов

МетодОписаниеТочность
Xcode Debug GaugeDebug → Debug Gauges → Core AnimationСредний FPS (округлён).
CADisplayLinkCADisplayLink(target:selector:)timestamp, targetTimestampТочный FPS, per-frame latency.
Instruments → Core Animation«Frame Rate», «Screen Recording»Профилирование в условиях, близких к реальным.
MTLCommandBuffer completionHandlerДля Metal-рендерингаНизкоуровневый контроль.

✅ Пример CADisplayLink-мониторинга:

let link = CADisplayLink(target: self, selector: #selector(frame))
link.add(to: .main, forMode: .common)

@objc func frame(_ link: CADisplayLink) {
let delta = link.targetTimestamp - link.timestamp // время до след. кадра
let fps = 1.0 / (link.duration + delta)
if fps < 55 { print("⚠️ FPS: \(fps)") }
}

8.1.2. Offscreen Rendering & Overdraw

ПризнакДиагностикаРешение
Offscreen renderingInstruments → Core Animation → «Color Offscreen-Rendered Yellow»Избегать masksToBounds + shadow, groupOpacity, UIVisualEffectView в скролле.
Overdraw«Color Blended Layers — Red»Уменьшить прозрачные слои, использовать .opaque флаг.
Misaligned images«Color Misaligned Images — Yellow/Green»Выравнивать по пикселям (floor(x)), использовать @2x/@3x.
Слишком большие текстуры«Color Copied Images — Blue»Сжимать изображения до MAX_TEXTURE_SIZE (обычно 4096×4096).

⚠️ Важно:

  • shouldRasterize = true → кэширует offscreen-рендеринг, но дорого при анимации.
  • draw(_:) в основном потоке → блокирует UI. Выносить в drawsAsynchronously = true (CALayer).

8.1.3. Texture Memory & GPU

ИнструментПоказательНорма
Instruments → Metal System TraceTexture Memory, Buffer Memory< 100 МБ на iPhone Pro, < 50 МБ на старых моделях.
Xcode Memory Report«GPU Memory»Не должен превышать 50% от IOSurface limit.
JetStream 2 (браузерный JS-бенчмарк)Не для iOS, но полезен для WebView-компонентов.

✅ Оптимизации:

  • Использовать UIImage.SymbolConfiguration.scale(.large) вместо растровых иконок.
  • CATiledLayer для больших изображений (карты, PDF).
  • UIGraphicsImageRendererFormat.preferredRange = .standard (не .extended) для экономии памяти.

8.1.4. Energy Impact

ПоказательДиагностикаПричины высокого потребления
CPU WakeupsEnergy Log → «CPU wakeups / sec»Таймеры (Timer), polling, CADisplayLink без паузы.
Location AccuracykCLLocationAccuracyBest в фонеИспользовать significantLocationChanges или reduced.
Network CallsBattery Usage → «Network»Частые мелкие запросы → объединять в batch.
Background Activity«Background energy use»beginBackgroundTask без endBackgroundTask.

✅ Рекомендации Apple:

  • Минимизировать DispatchSourceTimer с малым leeway.
  • Использовать URLSessionConfiguration.timeoutIntervalForResource для долгих задач.
  • Отключать CLLocationManager при applicationDidEnterBackground.

8.2. Crash Reporting: Symbolication, Форматы, Интеграции

8.2.1. Форматы крашей

ТипФайлОписание
Crash Report.crashСодержит threads, registers, binary images.
Diagnostic Report.ipsАналогично, но с дополнительными метаданными (iOS ≥12).
dSYM.dSYMОтладочные символы для symbolication.

🔍 Структура краша:

Thread 0 Crashed:
0 MyApp 0x0000000100005a34 0x100000000 + 23156
1 UIKitCore 0x0000000184e12abc -[UIApplication sendAction:to:forEvent:] + 96
...
Binary Images:
0x100000000 - 0x10000ffff MyApp arm64 <UUID> /var/containers/Bundle/Application/.../MyApp.app/MyApp

8.2.2. Symbolication

МетодКоманда
atosatos -arch arm64 -o MyApp.app.dSYM/Contents/Resources/DWARF/MyApp -l 0x100000000 0x0000000100005a34
symbolicatecrashsymbolicatecrash report.crash MyApp.app.dSYM
Xcode OrganizerАвтоматическая symbolication при upload в App Store Connect.

⚠️ Требования:

  • UUID бинарника в краше должен совпадать с UUID в dSYM (dwarfdump --uuid MyApp).
  • dSYM должен быть загружен в App Store Connect / Crashlytics / Sentry.

8.2.3. Интеграции

СервисОсобенности
Firebase CrashlyticsБесплатно, интеграция через CocoaPods/SPM, NDK-поддержка.
SentryПоддержка NDK, символы через Sentry CLI, кастомные tags/breadcrumbs.
PLCrashReporterЛокальный сбор крашей (open-source, используется в Crashlytics).
KSCrashAdvanced features: signal handling, C++ exceptions, Mach exceptions.

✅ Рекомендуемый workflow:

  1. Генерация dSYM при сборке (DEBUG_INFORMATION_FORMAT = dwarf-with-dsym).
  2. Upload dSYM в Crashlytics/Sentry (автоматически через upload-symbols скрипт).
  3. Symbolication в облаке → отчёты с именами методов и строками кода.

8.3. CI/CD: Xcode Cloud, Fastlane, xcodebuild

8.3.1. Xcode Cloud (Apple)

ВозможностьОграничения
Тестирование на реальных устройствахТолько для enrolled devices в Developer Account.
Триггеры: push, PR, ручной запускМакс. 500 минут/месяц (free tier).
Интеграция с TestFlightАвтоматическая загрузка сборок.
ci_post_xcodebuild.shКастомные скрипты после сборки.

✅ Конфигурация: .xccurrentversion + ci_scripts/ в репозитории.

8.3.2. Fastlane

КомандаНазначение
matchСинхронизация сертификатов и профилей через репозиторий.
gymСборка IPA (xcodebuild archive + export).
pilotЗагрузка в TestFlight.
deliverУправление метаданными App Store.
snapshotАвтоматические скриншоты для App Store.

✅ Пример Fastfile:

lane :beta do
match(type: "appstore")
gym(scheme: "MyApp", export_method: "app-store")
pilot
end

8.3.3. xcodebuild CLI — ключевые флаги

ФлагОписание
-scheme MyAppСхема для сборки.
-destination 'generic/platform=iOS'Целевая платформа.
-archivePath build/MyApp.xcarchiveПуть архива.
-exportOptionsPlist export.plistКонфигурация экспорта (ad-hoc, app-store, development).
-allowProvisioningUpdatesАвтоматическое обновление provisioning profiles.
-clonedSourcePackagesDirPath build/spmКэш SPM-зависимостей.

✅ Генерация export.plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>method</key>
<string>app-store</string>
<key>uploadBitcode</key>
<false/>
<key>compileBitcode</key>
<false/>
<key>provisioningProfiles</key>
<dict>
<key>com.example.MyApp</key>
<string>iOS_Distribution_Profile</string>
</dict>
</dict>
</plist>